//
//  LYDataBaseMigrationAction.m
//  Apps
//
//  Created by 李永彬 on 2019/6/5.
//  Copyright © 2019 appscomm. All rights reserved.
//

#import "LYDataBaseMigrationAction.h"
#import "LYDBMigrateTool.h"
#import "LYDatabase+Migrate.h"
#import "NSObject+LYDatabase.h"
#import <objc/runtime.h>
#import "LYDbSQL.h"


/**
 数据库迁移Action错误

 - LYMigrateAddColumnsErrorCode: 添加字段失败
 - LYMigrateDeleteColumnsErrorCode: 删除字段失败
 - LYMigrateChangeColumnsErrorCode: 修改字段名失败
 - LYMigrateRenameTableErrorCode: 修改表名失败
 - LYMigrateCustomInsertDataErrorCode: 自定义迁移插入数据失败错误
 */
typedef NS_ENUM(NSUInteger, LYMigrateDataBaseErrorCode) {

    LYMigrateAddColumnsErrorCode,

    LYMigrateDeleteColumnsErrorCode,

    LYMigrateChangeColumnsErrorCode,
    
    LYMigrateRenameTableErrorCode,
    
    LYMigrateCustomInsertDataErrorCode,
};

static NSString *LYDatabaseMigrateErrorDomain    = @"LYDatabaseMigrateErrorDomain";

@interface LYDataBaseMigrationAction ()

@property (strong, nonatomic) LYDatabase *database;

@end

@implementation LYDataBaseMigrationAction

- (instancetype)initWithDatabase:(LYDatabase *)database {
    self = [super init];
    if (self) {
        self.database = database;
    }
    return self;
}

//修改字段名
- (void)changeColumnsWithDic:(NSDictionary <NSDictionary *, NSDictionary *>*)changeDic migrateTableClass:(Class)tableClass error:(NSError * __autoreleasing *)error
{
    __block NSString *addSql = [[NSString alloc]init];
    NSString *updateSql = [[NSString alloc]init];
    NSMutableArray *oldColumnsNameArr = [NSMutableArray array];
    
    NSArray <NSDictionary *>* allKeys = [changeDic allKeys];
    
    for (NSDictionary *oldColumns in allKeys) {
        NSDictionary *newColumns = [changeDic objectForKey:oldColumns];
        
        //newColumns只有一个元素
        __block NSString *newColumnsName = [[NSString alloc]init];
        __block NSString *newColumnsType = [[NSString alloc]init];
        [newColumns enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
            addSql = [LYDBMigrateTool sqlAddColumnsName:key columnsType:obj tableName:[tableClass tableName]];
            newColumnsName = key;
            newColumnsType = obj;
        }];
        //添加字段
        BOOL addRes = [self.database executeUpdate:addSql];
        if (!addRes) {
            
            NSError *err = [NSError errorWithDomain:LYDatabaseMigrateErrorDomain code:LYMigrateChangeColumnsErrorCode userInfo:nil];
            *error = err;
            continue ;
        }
        
        //oldColumns只有一个元素
        __block NSString *oldColumnsName = [[NSString alloc]init];
        [oldColumns enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
            addSql = [LYDBMigrateTool sqlAddColumnsName:key columnsType:obj tableName:[tableClass tableName]];
            oldColumnsName = key;
            [oldColumnsNameArr addObject:key];
        }];
        
        updateSql = [LYDBMigrateTool sqlUpdateOldColumnsValueToNewColumnsValueWithTable:[tableClass tableName] oldColumn:oldColumnsName NewColumn:newColumnsName];
        
        //从旧字段的列表中拷贝值 到新字段列表
        BOOL updateRes = [self.database executeUpdate:updateSql];
        if (!updateRes) {
            
            NSError *err = [NSError errorWithDomain:LYDatabaseMigrateErrorDomain code:LYMigrateChangeColumnsErrorCode userInfo:nil];
            *error = err;
            continue ;
        }
        
    }
    
    //删除旧字段
    [self deleteColumnsWithArray:oldColumnsNameArr migrateTableClass:tableClass error:error];
    
}

//添加字段名
- (void)addColumnsWithDic:(NSDictionary <NSString *, NSString *>*)columnsDic migrateTableClass:(Class)tableClass error:(NSError * __autoreleasing *)error
{
    
    NSArray <NSString *>* allKeys = [columnsDic allKeys];
    
    for (NSString *columnsName in allKeys) {
        NSString *columnsType = [columnsDic objectForKey:columnsName];
        NSString *addSql = [LYDBMigrateTool sqlAddColumnsName:columnsName columnsType:columnsType tableName:[tableClass tableName]];
        BOOL res = [self.database executeUpdate:addSql];;
        if (res) {
            NSLog(@"新增%@表字段%@成功",[tableClass tableName],columnsName);
        }else{
            NSError *err = [NSError errorWithDomain:LYDatabaseMigrateErrorDomain code:LYMigrateAddColumnsErrorCode userInfo:nil];
            *error = err;
            
            NSLog(@"新增%@表字段%@失败",[tableClass tableName],columnsName);
        }
    }
    
}

//删除字段名
- (void)deleteColumnsWithArray:(NSArray *)columnsDelete migrateTableClass:(Class)tableClass error:(NSError * __autoreleasing *)error
{
    //把表重命名
    NSString *tempTableName = [NSString stringWithFormat:@"%@_bak",[tableClass tableName]];
    NSString *renameSql = [LYDBMigrateTool sqlRenameOldTable:[tableClass tableName] newTable:tempTableName];
    BOOL renameRes = [self.database executeUpdate:renameSql];
    if (!renameRes) {
        
        NSError *err = [NSError errorWithDomain:LYDatabaseMigrateErrorDomain code:LYMigrateDeleteColumnsErrorCode userInfo:nil];
        *error = err;
        return;
    }
    
    //从临时表中拷贝不包含删除字段的数据
    NSArray *columnsAll = [self.database columnsWithTable:tempTableName];
    NSArray *newColumnsArray = [LYDBMigrateTool filterFromArray:columnsAll withOutArray:columnsDelete];
    NSString *deleteSql = [LYDBMigrateTool sqlDeleteColumns:newColumnsArray tableName:[tableClass tableName] tempTable:tempTableName];
    BOOL deleteRes = [self.database executeUpdate:deleteSql];
    if (deleteRes) {
    }
    else {
        //如果拷贝失败后把临时表还原
        NSString *backSql = [LYDBMigrateTool sqlRenameOldTable:tempTableName newTable:[tableClass tableName]];
        BOOL backRes = [self.database executeUpdate:backSql];
        NSLog(@"%d",backRes);
        NSError *err = [NSError errorWithDomain:LYDatabaseMigrateErrorDomain code:LYMigrateDeleteColumnsErrorCode userInfo:nil];
        *error = err;
        
    }
    
    //如果存在临时表，则删除临时表 完成操作
    NSString* sql = [LYDBMigrateTool sqlDropTable:tempTableName];
    [self.database executeUpdate:sql];
}

- (void)dropTableWithTableClasses:(NSArray <Class>*)tableClasses error:(NSError * __autoreleasing *)error {
    
    for (Class class in tableClasses) {
        NSString *tableName = [class tableName];
        NSString *dropSQL = [NSString stringWithFormat:@"DROP TABLE IF EXISTS %@;",tableName];
        [self.database executeUpdate:dropSQL];
    }
    
}

- (void)customRenewMigrateTableWithClass:(Class)tableClass pageSize:(NSInteger)pageSize dropOldTableWhenNoError:(BOOL)dropOldTable handler:(NSArray * _Nullable (^)(NSInteger page, NSArray <NSDictionary *>*originData))handler errorCapture:(void(^)(NSError *error, BOOL *needStop))errorCapture progress:(void(^)(NSProgress *completeProgress))progress completion:(void(^)(void))completion {
    
    // 动态创建一个类
    NSString *originClassName = NSStringFromClass(tableClass);
    NSString *tempClassName = [originClassName stringByAppendingString:@"MigrateTempTable"];
    const char *tmpClsName = [tempClassName UTF8String];
    
    Class tmpCls = objc_getClass(tmpClsName);
    if (!tmpCls) {
        tmpCls = objc_allocateClassPair(tableClass, tmpClsName, 0);
        objc_registerClassPair(tmpCls);
    }
    
    // 只有执行了这句代码，tmpCls这个动态创建的类，才可以调用tableName方法，拿到他的表名
    [[LYDbSQL sharedInstance] addClassName:tempClassName tableName:[NSString stringWithFormat:@"t_%@",tempClassName]];
    
    NSInteger rowCount = [tableClass selectRowCount];
    
    NSString* renameTableSql = [LYDBMigrateTool sqlRenameOldTable:[tableClass tableName] newTable:[tmpCls tableName]];
    BOOL renameSuccess = [self.database executeUpdate:renameTableSql];
    NSError *err = nil;

    if (renameSuccess == NO) {
        err = [NSError errorWithDomain:LYDatabaseMigrateErrorDomain code:LYMigrateRenameTableErrorCode userInfo:nil];
        BOOL needStop = NO;
        if (errorCapture) {
            errorCapture(err, &needStop);
        }
        
        if (needStop) {
            if (completion) {
                completion();
            }
            return;
        }
    }
        
    err = nil;
    
    [tableClass createTable];
    
    NSInteger pageCount = ceil(((double)rowCount)/pageSize);
    
    NSProgress *completeProgress = [NSProgress progressWithTotalUnitCount:pageCount];
    for (NSInteger page = 0; page < pageCount; page ++) {
        
        NSArray <NSDictionary *>*singlePageData = [tmpCls selectOriginWithOffset:page * pageSize limit:pageSize];
        
        NSArray *models = nil;
        
        if (handler) {
            models = handler(page, singlePageData);
        }
        
        if (models.count) {
            NSInteger successCount = [tableClass saveBatch:models];
            if (successCount != models.count) {
                
                NSLog(@"自定义迁移插入出错,page = %@",@(page));
                err = [NSError errorWithDomain:LYDatabaseMigrateErrorDomain code:LYMigrateCustomInsertDataErrorCode userInfo:nil];

                if (errorCapture) {
                    BOOL needStop = NO;
                    errorCapture(err, &needStop);
                    if (needStop) {
                        [completeProgress cancel];
                        break;
                    }
                }
            }
            else {
                if (progress) {
                    progress(completeProgress);
                }
            }
        }
        completeProgress.completedUnitCount = page + 1;
    }
    
    if (dropOldTable && err == nil) {
        NSString* sql = [LYDBMigrateTool sqlDropTable:[tmpCls tableName]];
        [self.database executeUpdate:sql];
    }
    
    if (completion) {
        completion();
    }
}

@end
